using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Assignment2
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;        

        //FontEffects fe;
        //SpriteFont font;

        //Vector2 keyTxtPos, mouseTxtPos, gamepadTxtPos;
        //string keyboardTxt, mouseTxt, gamepadTxt;
        //int keyboardCount, mouseCount, gamepadCount;

        Vector3 cameraPosition;
        Vector3 cameraFront;

        InputProcessor input;

        BasicEffect effect;        
        float Yangle;
        float Xangle;
        float Zangle;


        public Vector3 size;
        public Vector3 position;
        public VertexPositionNormalTexture[] vertices;
        public int primitiveCount;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);            
            Content.RootDirectory = "Content";

            input = new InputProcessor(this);
            Components.Add(input);

            //keyboardCount = 0;
            //mouseCount = 0;
            //gamepadCount = 0;

            //keyboardTxt = "Space Bar Pressed: " + keyboardCount + "Time(s)";
            //mouseTxt = "Left Mouse Clicked: " + mouseCount + "Time(s)";
            //gamepadTxt = "A Button Pressed: " + gamepadCount + "Time(s)";

            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;

            Xangle = .0f;
            Yangle = .0f;
            Zangle = .0f;

            cameraPosition = new Vector3(0, .5f, 2);
            cameraFront = new Vector3(0, .5f, 0);

            this.size = new Vector3(1, 1, 1);
            this.position = new Vector3(0, 0, 0);
            this.vertices = null;

            primitiveCount = 12;
            vertices = new VertexPositionNormalTexture[36];
            Vector2 Texcoords = new Vector2(0f, 0f);

            Vector3[] face = new Vector3[6];
            //TopLeft
            face[0] = new Vector3(-size.X, size.Y, 0.0f);
            //BottomLeft
            face[1] = new Vector3(-size.X, -size.Y, 0.0f);
            //TopRight
            face[2] = new Vector3(size.X, size.Y, 0.0f);
            //BottomLeft
            face[3] = new Vector3(-size.X, -size.Y, 0.0f);
            //BottomRight
            face[4] = new Vector3(size.X, -size.Y, 0.0f);
            //TopRight
            face[5] = new Vector3(size.X, size.Y, 0.0f);

            //front face
            for (int i = 0; i <= 2; i++)
            {
                vertices[i] = new VertexPositionNormalTexture(face[i] + Vector3.UnitZ * size.Z + position, Vector3.UnitZ, Texcoords);
                vertices[i + 3] = new VertexPositionNormalTexture(face[i + 3] + Vector3.UnitZ * size.Z + position, Vector3.UnitZ, Texcoords);
            }

            //back face

            for (int i = 0; i <= 2; i++)
            {
                vertices[i + 6] = new VertexPositionNormalTexture(face[2 - i] - Vector3.UnitZ * size.Z + position, -Vector3.UnitZ, Texcoords);
                vertices[i + 6 + 3] = new VertexPositionNormalTexture(face[5 - i] - Vector3.UnitZ * size.Z + position, -Vector3.UnitZ, Texcoords);
            }

            //left face
            Matrix RotY90 = Matrix.CreateRotationY(-(float)Math.PI / 2f);
            for (int i = 0; i <= 2; i++)
            {
                vertices[i + 12] = new VertexPositionNormalTexture(Vector3.Transform(face[i], RotY90) - Vector3.UnitX * size.X + position, -Vector3.UnitX, Texcoords);
                vertices[i + 12 + 3] = new VertexPositionNormalTexture(Vector3.Transform(face[i + 3], RotY90) - Vector3.UnitX * size.X + position, -Vector3.UnitX, Texcoords);
            }

            //Right face

            for (int i = 0; i <= 2; i++)
            {
                vertices[i + 18] = new VertexPositionNormalTexture(Vector3.Transform(face[2 - i], RotY90) + Vector3.UnitX * size.X + position, Vector3.UnitX, Texcoords);
                vertices[i + 18 + 3] = new VertexPositionNormalTexture(Vector3.Transform(face[5 - i], RotY90) + Vector3.UnitX * size.X + position, Vector3.UnitX, Texcoords);

            }

            //Top face

            Matrix RotX90 = Matrix.CreateRotationX(-(float)Math.PI / 2f);
            for (int i = 0; i <= 2; i++)
            {
                vertices[i + 24] = new VertexPositionNormalTexture(Vector3.Transform(face[i], RotX90) + Vector3.UnitY * size.Y + position, Vector3.UnitY, Texcoords);
                vertices[i + 24 + 3] = new VertexPositionNormalTexture(Vector3.Transform(face[i + 3], RotX90) + Vector3.UnitY * size.Y + position, Vector3.UnitY, Texcoords);

            }

            //Bottom face

            for (int i = 0; i <= 2; i++)
            {
                vertices[i + 30] = new VertexPositionNormalTexture(Vector3.Transform(face[2 - i], RotX90) - Vector3.UnitY * size.Y + position, -Vector3.UnitY, Texcoords);
                vertices[i + 30 + 3] = new VertexPositionNormalTexture(Vector3.Transform(face[5 - i], RotX90) - Vector3.UnitY * size.Y + position, -Vector3.UnitY, Texcoords);
            }
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            //Vector3 diffVec = new Vector3(1f, 1f, 1f);
            effect = new BasicEffect(graphics.GraphicsDevice);
            //effect.AmbientLightColor = new Vector3(0.2f, 0.1f, 0.6f);
            //effect.DirectionalLight0.Enabled = true;
            //effect.DirectionalLight0.DiffuseColor = diffVec;
            //effect.DirectionalLight0.Direction = Vector3.Normalize(Vector3.One);
            //effect.LightingEnabled = true;
            //Matrix projection = Matrix.CreatePerspectiveFieldOfView((float)Math.PI / 4.0f,
            //                    (float)this.Window.ClientBounds.Width / (float)this.Window.ClientBounds.Height, 1f, 100f);
            //effect.Projection = projection;
            //Matrix V = Matrix.CreateTranslation(0f, 0f, -10f);
            //effect.View = V;

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
                         
            // TODO: use this.Content to load your game content here
            //font = Content.Load<SpriteFont>("Font");

            
            //keyTxtPos = new Vector2(0, 0 - font.MeasureString("STRING").Y / 2);

            //mouseTxtPos = new Vector2(keyTxtPos.X, keyTxtPos.Y + font.MeasureString("S").Y / 2);
            //gamepadTxtPos = new Vector2(mouseTxtPos.X, mouseTxtPos.Y + font.MeasureString("S").Y / 2);

            //fe = new FontEffects(this, keyTxtPos, font, 5, Color.Orange, Color.Gray, .2f, 1f, 1f, "");
            
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit            
            if(input.isButtonPressed(Buttons.Back, PlayerIndex.One) || input.isKeyPressed(Keys.Escape))         
                this.Exit();

            //if (input.isKeyPressed(Keys.Space))
            //    keyboardCount++;
            //if (input.isLeftMouseClicked())
            //    mouseCount++;
            //if (input.isButtonPressed(Buttons.A, PlayerIndex.One))
            //    gamepadCount++;

            //keyboardTxt = "Space Bar Pressed: " + keyboardCount + " Time(s)";
            //mouseTxt = "Left Mouse Clicked: " + mouseCount + " Time(s)";
            //gamepadTxt = "A Button Pressed: " + gamepadCount + " Time(s)";

            //Move the camera
            if (input.isKeyDown(Keys.Left) || input.isRightThumbstickMovesLeft(PlayerIndex.One))
            {
                cameraPosition.X += .05f;
                cameraFront.X += .05f;
            }
            else if (input.isKeyDown(Keys.Right) || input.isRightThumbstickMovesRight(PlayerIndex.One))
            {
                cameraPosition.X -= .05f;
                cameraFront.X -= .05f;
            }
            if (input.isKeyDown(Keys.Up) || input.isRightThumbstickMovesUp(PlayerIndex.One))
            {
                cameraPosition.Y -= .05f;
                cameraFront.Y -= .05f;
            }
            else if (input.isKeyDown(Keys.Down) || input.isRightThumbstickMovesDown(PlayerIndex.One))
            {
                cameraPosition.Y += .05f;
                cameraFront.Y += .05f;
            }
            if (input.isKeyDown(Keys.PageUp) || input.isButtonDown(Buttons.RightShoulder, PlayerIndex.One))
            {
                cameraPosition.Z -= .05f;
                cameraFront.Z -= .05f;
            }
            else if (input.isKeyDown(Keys.PageDown) || input.isButtonDown(Buttons.LeftShoulder, PlayerIndex.One))
            {
                cameraPosition.Z += .05f;
                cameraFront.Z += .05f;
            }

            //Rotate the text
            //if (input.isKeyDown(Keys.A) || input.isLeftThumbstickMovesLeft(PlayerIndex.One))
            //    Yangle -= .05f;
            //else if (input.isKeyDown(Keys.D) || input.isLeftThumbstickMovesRight(PlayerIndex.One))
            //    Yangle += .05f;
            //if (input.isKeyDown(Keys.W) || input.isLeftThumbstickMovesUp(PlayerIndex.One))
            //    Xangle -= .05f;
            //else if (input.isKeyDown(Keys.S) || input.isLeftThumbstickMovesDown(PlayerIndex.One))
            //    Xangle += .05f;
            //if (input.isKeyDown(Keys.Q) || input.isButtonDown(Buttons.LeftTrigger, PlayerIndex.One))
            //    Zangle += .05f;
            //else if (input.isKeyDown(Keys.E) || input.isButtonDown(Buttons.RightTrigger, PlayerIndex.One))
            //    Zangle -= .05f;

            //mouseTxtPos = new Vector2(keyTxtPos.X, mouseTxtPos.Y);
            //gamepadTxtPos = new Vector2(mouseTxtPos.X, gamepadTxtPos.Y);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            Vector3 textPosition = new Vector3(0, 0, -10);


            Matrix view = Matrix.CreateLookAt(cameraPosition,
                                              cameraFront,
                                              Vector3.Up);

            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
                                                                    GraphicsDevice.Viewport.AspectRatio,
                                                                    1, 1000);

            Matrix invertY = Matrix.CreateScale(1, 1, 1);

            Matrix rotation = Matrix.CreateRotationX(Xangle) * Matrix.CreateRotationY(Yangle) * Matrix.CreateRotationZ(Zangle) ;

            effect.World = invertY * Matrix.CreateTranslation(textPosition) * rotation;
            effect.View = view;
            effect.Projection = projection;

            if (vertices != null)
            {
                RasterizerState rs = new RasterizerState();
                rs.CullMode = CullMode.CullClockwiseFace;
                GraphicsDevice.RasterizerState = rs;
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, primitiveCount);
                }
            }
            
            

            //spriteBatch.Begin(0, null, null, DepthStencilState.DepthRead, RasterizerState.CullNone, basicEffect);

            ////fe.text = keyboardTxt;
            ////fe.SetFontPosition(keyTxtPos);
            ////fe.Draw(gameTime, spriteBatch);
            ////fe.text = gamepadTxt;
            ////fe.SetFontPosition(gamepadTxtPos);
            ////fe.Draw(gameTime, spriteBatch);
            ////fe.text = mouseTxt;
            ////fe.SetFontPosition(mouseTxtPos);
            ////fe.Draw(gameTime, spriteBatch);                                   

            //spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
